#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <unordered_map>
#include <cmath>
#include <ctime>

#include "constants.h"
#include "classes.h"
#include "data_io.h"

using namespace std;

SiteInfo site_info;
ClientInfo client_info;
DemandInfo demand_info;
ConfigInfo config_info;
int M;
int N;
int T;

istream &_read_a_line(ifstream &ifs, string &line)
{
    istream &ist = getline(ifs, line, '\n');
    if (line.back() == '\r')
    {
        line.pop_back(); // 去除末尾\r
    }
    return ist;
};

void _read_demand()
{
    ifstream demand_fs(DEMAND_FILE, ios::binary);
    string line, field;

    _read_a_line(demand_fs, line);

    stringstream line_ss(line);
    getline(line_ss, field, ',');
    getline(line_ss, field, ',');

    while (getline(line_ss, field, ','))
    {
        client_info.add_client(field);
    }

    int t = 0;
    int stream_id = 0;
    string last_timestep = "_empty_";
    string current_timestep = last_timestep;
    string stream_name, value;
    TDemand *tdemand = new TDemand(t);
    while (_read_a_line(demand_fs, line))
    {
        stringstream line_ss(line);
        getline(line_ss, current_timestep, ',');
        if ((last_timestep != current_timestep) && (last_timestep != "_empty_"))
        {
            demand_info.add_demand(tdemand);
            t++;
            stream_id = 0;
            tdemand = new TDemand(t);
        }
        getline(line_ss, stream_name, ',');
        tdemand->add_stream(stream_name);

        for (int client_id = 0; client_id < client_info.M; client_id++)
        {
            getline(line_ss, value, ',');
            int bw = stoi(value);
            tdemand->add_demand(stream_id, client_id, bw);
        }
        last_timestep = current_timestep;
        stream_id++;
    }
    demand_info.add_demand(tdemand);
    config_info.set_quantile_index(demand_info.T);
    demand_fs.close();
};

void _read_config()
{
    ifstream config_fs(CONFIG_FILE, ios::binary);
    string line, field;

    _read_a_line(config_fs, line);
    unordered_map<string, string> config_kv;
    string key, value;
    while (_read_a_line(config_fs, line))
    {
        stringstream line_ss(line);
        getline(line_ss, key, '=');
        getline(line_ss, value, '=');
        config_kv[key] = value;
    }
    config_fs.close();

    config_info.qos_constraint = stoi(config_kv["qos_constraint"]);
    config_info.base_cost = stoi(config_kv["base_cost"]);
    config_info.center_cost = stod(config_kv["center_cost"]);
};

void _read_site_bandwidth()
{
    string line, site_name, bandwidth;
    ifstream site_bandwidth_fs(SITE_BANDWIDTH_FILE, ios::binary);
    _read_a_line(site_bandwidth_fs, line);

    for (int site_id = 0; _read_a_line(site_bandwidth_fs, line); site_id++)
    {
        stringstream line_ss(line);
        getline(line_ss, site_name, ',');
        getline(line_ss, bandwidth, ',');
        site_info.add_site(site_name, stoi(bandwidth));
    }
    site_bandwidth_fs.close();
};

void _read_qos()
{
    string line, site_name, client_name, field;
    int qos;

    ifstream qos_fs(QOS_FILE, ios::binary);
    _read_a_line(qos_fs, line);

    unordered_map<int, string> _tmp_idx_to_client_name;

    stringstream header_ss(line);
    getline(header_ss, field, ',');

    for (int _idx = 0; getline(header_ss, client_name, ','); _idx++)
    {
        _tmp_idx_to_client_name[_idx] = client_name;
    }

    while (_read_a_line(qos_fs, line))
    {
        stringstream line_ss(line);
        getline(line_ss, site_name, ',');

        for (int _idx = 0; getline(line_ss, field, ','); _idx++)
        {
            qos = stoi(field);
            if (qos < config_info.qos_constraint)
            {
                int client_id = client_info.client_name_to_id[_tmp_idx_to_client_name[_idx]];
                int site_id = site_info.site_name_to_id[site_name];

                client_info.add_connect(client_id, site_id);
                site_info.add_connect(site_id, client_id);
                site_info.site_reachable_matrix[site_id][client_id] = true;
            }
        }
    }
    qos_fs.close();
};

void read_all_data()
{
    cout << "data dir: " << DATADIR << endl;
    _read_demand();
    _read_config();
    _read_site_bandwidth();
    _read_qos();

    M = client_info.M;
    N = site_info.N;
    T = demand_info.T;
};

void output(AssignInfo &assign_info)
{
    cout << "output file: " << OUTPUT_FILE << endl;
    ofstream out_file(OUTPUT_FILE, ios::out);

    clock_t end_time = clock(); //计时结束
    cout << "The run time is: " << (double)(end_time - STARTTIME) / CLOCKS_PER_SEC << "s" << endl;

    for (int t = 0; t < T; t++)
    {
        TAssign *tassign = assign_info.assign_vec[t];
        TDemand *tdemand = demand_info.demand_vec[t];

        for (int client_id = 0; client_id < M; client_id++)
        {
            unordered_map<int, vector<int>> site_allo_streams;
            for (int stream_id = 0; stream_id < tdemand->P; stream_id++)
            {
                int site_id = tassign->stream_client_to_allo_node[stream_id][client_id].site_id;
                if (site_id == -1)
                    continue;
                if (site_allo_streams.find(site_id) == site_allo_streams.end())
                {
                    site_allo_streams[site_id] = vector<int>();
                }
                site_allo_streams[site_id].push_back(stream_id);
            }

            out_file << client_info.client_names[client_id] << ":";
            bool _flag = false;

            for (auto &iter : site_allo_streams)
            {
                if (_flag)
                    out_file << ",";
                _flag = true;

                out_file << "<" << site_info.site_names[iter.first];
                for (auto &stream_id : iter.second)
                {
                    out_file << "," << tdemand->stream_name_vec[stream_id];
                }
                out_file << ">";
            }

            out_file << endl;
        }
    }
    out_file.close();
}